import Api, { HttpResponse } from '../../http/Api';
import { tokenInterceptor } from '../Interceptors';
import { HttpUtil } from '../../utils/HttpUtil';
import {
	HttpServiceResponse,
	PageInfo,
	QueryAllCondition,
	QueryCondition,
	TimeModel,
} from './index';
import { Update } from '../../utils/ObjectUtil';
import { DateUtil } from '../../utils/DateUtil';

abstract class BaseService<T extends TimeModel, K extends keyof T> extends Api {
	protected initTokenInstance = (key: string): Api => {
		return this.initSingle(
			key,
			{
				baseURL: `/api/${key}`,
			},
			tokenInterceptor
		) as Api;
	};

	protected abstract format(o: T): Update<T, K, string>;

	protected abstract time(o: Update<T, K, string>): T;

	selectOneById = (id: string): Promise<HttpServiceResponse<T>> =>
		this.request({
			method: this.method.GET,
			url: `/id/${id}`,
		}).then((resp: HttpServiceResponse<any>) => {
			HttpUtil.setData(resp, this.time(HttpUtil.getData(resp)));
			return resp;
		});

	selectOne = (record: Partial<T>): Promise<HttpServiceResponse<T>> =>
		this.request({
			method: this.method.POST,
			url: '/search',
			data: record ? this.format(record as T) : null,
		}).then((resp: HttpServiceResponse<any>) => {
			HttpUtil.setData(resp, this.time(HttpUtil.getData(resp)));
			return resp;
		});

	selectByIds = (list: string[]): Promise<HttpServiceResponse<T[]>> =>
		this.request({
			method: this.method.POST,
			url: '/list/id',
			data: list,
		}).then((resp: HttpServiceResponse<any[]>) => {
			HttpUtil.setData(
				resp,
				HttpUtil.getData(resp).map(record => this.time(record))
			);
			return resp;
		});

	select = (
		condition: QueryCondition<T>
	): Promise<HttpServiceResponse<PageInfo<T>>> =>
		this.request({
			method: this.method.POST,
			url: '/list/search',
			data: {
				...condition,
				query: condition.query ? this.format(condition.query as T) : null,
				columnCondition: {
					...condition.columnCondition,
					timeConditions: condition.columnCondition?.timeConditions
						? condition.columnCondition.timeConditions.map(timeCondition =>
								DateUtil.formatObject(timeCondition, ['begin', 'end'])
						  )
						: null,
				},
			},
		}).then((resp: HttpServiceResponse<PageInfo<any>>) => {
			HttpUtil.setList(
				resp,
				HttpUtil.getList(resp).map(record => this.time(record))
			);
			return resp;
		});

	selectLike = (
		condition: QueryCondition<T>
	): Promise<HttpServiceResponse<PageInfo<T>>> =>
		this.request({
			method: this.method.POST,
			url: '/list/like',
			data: {
				...condition,
				query: condition.query ? this.format(condition.query as T) : null,
				columnCondition: {
					...condition.columnCondition,
					timeConditions: condition.columnCondition?.timeConditions
						? condition.columnCondition.timeConditions.map(timeCondition =>
								DateUtil.formatObject(timeCondition, ['begin', 'end'])
						  )
						: null,
				},
			},
		}).then((resp: HttpServiceResponse<PageInfo<any>>) => {
			HttpUtil.setList(
				resp,
				HttpUtil.getList(resp).map(record => this.time(record))
			);
			return resp;
		});

	selectAll = (
		condition: QueryAllCondition
	): Promise<HttpServiceResponse<PageInfo<T>>> =>
		this.request({
			method: this.method.GET,
			url: '/list',
			data: condition,
		}).then((resp: HttpServiceResponse<PageInfo<any>>) => {
			HttpUtil.setList(
				resp,
				HttpUtil.getList(resp).map(record => this.time(record))
			);
			return resp;
		});

	selectCount = (record: Partial<T>): Promise<HttpServiceResponse<number>> =>
		this.request({
			method: this.method.POST,
			url: '/count',
			data: record ? this.format(record as T) : null,
		});

	insert = (record: Partial<T>): Promise<HttpServiceResponse<string>> =>
		this.request({
			method: this.method.POST,
			data: record ? this.format(record as T) : null,
		});

	insertList = (
		records: Partial<T>[]
	): Promise<HttpServiceResponse<string[]>> =>
		this.request({
			method: this.method.POST,
			url: '/list',
			data: records.map(record => (record ? this.format(record as T) : null)),
		});

	save = (record: Partial<T>): Promise<HttpServiceResponse<string>> =>
		this.request({
			method: this.method.PUT,
			data: record ? this.format(record as T) : null,
		});

	saveList = (records: Partial<T>[]): Promise<HttpServiceResponse<string[]>> =>
		this.request({
			method: this.method.PUT,
			url: '/list',
			data: records.map(record => (record ? this.format(record as T) : null)),
		});

	update = (record: Partial<T>): Promise<HttpServiceResponse<void>> =>
		this.request({
			method: this.method.PATCH,
			data: record ? this.format(record as T) : null,
		});

	updateList = (records: Partial<T>[]): Promise<HttpServiceResponse<void>> =>
		this.request({
			method: this.method.PATCH,
			url: '/list',
			data: records.map(record => (record ? this.format(record as T) : null)),
		});

	deleteById = (id: string): Promise<HttpResponse<void>> =>
		this.request({
			method: this.method.DELETE,
			url: `/id/${id}`,
		});

	deleteByIds = (list: string[]): Promise<HttpResponse<void>> =>
		this.request({
			method: this.method.DELETE,
			url: '/list/id',
			data: list,
		});
}

export abstract class TimeDataService<
	S extends BaseService<T, K>,
	T extends TimeModel,
	K extends keyof T
> extends BaseService<T, K> {
	public constructor() {
		super();
		this.create();
	}

	protected abstract create(): S;
}
